package com.gitee.wsl.coroutines.job.sheduler

import com.gitee.wsl.coroutines.job.GroupMatcher
import com.gitee.wsl.coroutines.job.IInterruptableJob
import com.gitee.wsl.coroutines.job.IJob
import com.gitee.wsl.coroutines.job.IJobExecutionContext
import com.gitee.wsl.coroutines.job.IJobFactory
import com.gitee.wsl.coroutines.job.JobDataMap
import com.gitee.wsl.coroutines.job.JobDetail
import com.gitee.wsl.coroutines.job.JobKey
import com.gitee.wsl.coroutines.job.exception.SchedulerException
import com.gitee.wsl.coroutines.job.exception.UnableToInterruptJobException
import com.gitee.wsl.coroutines.job.listener.IListenerManager
import com.gitee.wsl.coroutines.job.trigger.ITrigger
import com.gitee.wsl.coroutines.job.trigger.ITrigger.ETriggerState
import com.gitee.wsl.coroutines.job.trigger.TriggerKey
import com.gitee.wsl.time.calendar.Calendar
import kotlin.time.ExperimentalTime
import kotlin.time.Instant


@OptIn(ExperimentalTime::class)
interface IScheduler {
    @get:Throws(SchedulerException::class)
    val schedulerName: String

    @get:Throws(SchedulerException::class)
    val schedulerInstanceId: String

    @get:Throws(SchedulerException::class)
    val context: SchedulerContext

    /// Scheduler State Management Methods
    /**
     * Starts the `Scheduler`'s threads that fire
     * `[ITrigger]s`. When a scheduler is first created it is in
     * "stand-by" mode, and will not fire triggers. The scheduler can also be put
     * into stand-by mode by calling the `standby()` method.
     *
     *
     * The misfire/recovery process will be started, if it is the initial call to
     * this method on this scheduler instance.
     *
     *
     * @throws SchedulerException
     * if `shutdown()` has been called, or there is an error
     * within the `Scheduler`.
     * @see .startDelayed
     * @see .standby
     * @see .shutdown
     */
    @Throws(SchedulerException::class)
    fun start()

    /**
     * Calls {#start()} after the indicated number of seconds. (This call does not
     * block). This can be useful within applications that have initializers that
     * create the scheduler immediately, before the resources needed by the
     * executing jobs have been fully initialized.
     *
     * @throws SchedulerException
     * if `shutdown()` has been called, or there is an error
     * within the `Scheduler`.
     * @see .start
     * @see .standby
     * @see .shutdown
     */
    @Throws(SchedulerException::class)
    fun startDelayed(seconds: Int)

    @get:Throws(SchedulerException::class)
    val isStarted: Boolean

    /**
     * Temporarily halts the `Scheduler`'s firing of
     * `[ITrigger]s`.
     *
     *
     * When `start()` is called (to bring the scheduler out of stand-by
     * mode), trigger misfire instructions will NOT be applied during the
     * execution of the `start()` method - any misfires will be
     * detected immediately afterward (by the `JobStore`'s normal
     * process).
     *
     *
     *
     * The scheduler is not destroyed, and can be re-started at any time.
     *
     *
     * @see .start
     * @see .pauseAll
     */
    @Throws(SchedulerException::class)
    fun standby()

    @get:Throws(SchedulerException::class)
    val isInStandbyMode: Boolean

    /**
     * Halts the `Scheduler`'s firing of
     * `[ITrigger]s`, and cleans up all resources associated with
     * the Scheduler. Equivalent to `shutdown(false)`.
     *
     *
     * The scheduler cannot be re-started.
     *
     *
     * @see .shutdown
     */
    @Throws(SchedulerException::class)
    fun shutdown()

    /**
     * Halts the `Scheduler`'s firing of
     * `[ITrigger]s`, and cleans up all resources associated with
     * the Scheduler.
     *
     *
     * The scheduler cannot be re-started.
     *
     *
     * @param waitForJobsToComplete
     * if `true` the scheduler will not allow this method to
     * return until all currently executing jobs have completed.
     * @see .shutdown
     */
    @Throws(SchedulerException::class)
    fun shutdown(waitForJobsToComplete: Boolean)

    @get:Throws(SchedulerException::class)
    val isShutdown: Boolean

    @get:Throws(SchedulerException::class)
    val metaData: SchedulerMetaData

    @get:Throws(SchedulerException::class)
    val currentlyExecutingJobs: List<IJobExecutionContext>

    /**
     * Set the `JobFactory` that will be responsible for producing
     * instances of `Job` classes.
     *
     *
     * JobFactories may be of use to those wishing to have their application
     * produce `Job` instances via some special mechanism, such as to
     * give the opportunity for dependency injection.
     *
     *
     * @see com.helger.quartz.spi.IJobFactory
     */
    @Throws(SchedulerException::class)
    fun setJobFactory(factory: IJobFactory)

    @get:Throws(SchedulerException::class)
    val listenerManager: IListenerManager

    ///////////////////////////////////////////////////////////////////////////
    ///
    /// Scheduling-related Methods
    ///
    ///////////////////////////////////////////////////////////////////////////
    /**
     * Add the given `[com.helger.quartz.IJobDetail]` to the
     * Scheduler, and associate the given `[ITrigger]` with it.
     *
     *
     * If the given Trigger does not reference any `Job`, then it will
     * be set to reference the Job passed with it into this method.
     *
     *
     * @throws SchedulerException
     * if the Job or Trigger cannot be added to the Scheduler, or there is
     * an internal Scheduler error.
     */
    @Throws(SchedulerException::class)
    fun scheduleJob(jobDetail: JobDetail, trigger: ITrigger): Instant

    /**
     * Schedule the given `[com.helger.quartz.ITrigger]` with the
     * `Job` identified by the `Trigger`'s settings.
     *
     * @throws SchedulerException
     * if the indicated Job does not exist, or the Trigger cannot be added
     * to the Scheduler, or there is an internal Scheduler error.
     */
    @Throws(SchedulerException::class)
    fun scheduleJob(trigger: ITrigger): Instant

    /**
     * Schedule all of the given jobs with the related set of triggers.
     *
     *
     * If any of the given jobs or triggers already exist (or more specifically,
     * if the keys are not unique) and the replace parameter is not set to true
     * then an exception will be thrown.
     *
     *
     * @throws ObjectAlreadyExistsException
     * if the job/trigger keys are not unique and the replace flag is not
     * set to true.
     */
    @Throws(SchedulerException::class)
    fun scheduleJobs(
        triggersAndJobs: Map<JobDetail, Set<ITrigger>>,
        replace: Boolean,
    )

    /**
     * Schedule the given job with the related set of triggers.
     *
     *
     * If any of the given job or triggers already exist (or more specifically, if
     * the keys are not unique) and the replace parameter is not set to true then
     * an exception will be thrown.
     *
     *
     * @throws ObjectAlreadyExistsException
     * if the job/trigger keys are not unique and the replace flag is not
     * set to true.
     */
    @Throws(SchedulerException::class)
    fun scheduleJob(
        jobDetail: JobDetail,
        triggersForJob: Set<ITrigger>,
        replace: Boolean,
    )

    /**
     * Remove the indicated `[ITrigger]` from the scheduler.
     *
     *
     * If the related job does not have any other triggers, and the job is not
     * durable, then the job will also be deleted.
     *
     */
    @Throws(SchedulerException::class)
    fun unscheduleJob(triggerKey: TriggerKey): Boolean

    /**
     * Remove all of the indicated `[ITrigger]`s from the
     * scheduler.
     *
     *
     * If the related job does not have any other triggers, and the job is not
     * durable, then the job will also be deleted.
     *
     *
     *
     * Note that while this bulk operation is likely more efficient than invoking
     * `unscheduleJob(TriggerKey triggerKey)` several times, it may
     * have the adverse affect of holding data locks for a single long duration of
     * time (rather than lots of small durations of time).
     *
     */
    @Throws(SchedulerException::class)
    fun unscheduleJobs(triggerKeys: List<TriggerKey>): Boolean

    /**
     * Remove (delete) the `[com.helger.quartz.ITrigger]` with
     * the given key, and store the new given one - which must be associated with
     * the same job (the new trigger must have the job name &amp; group specified)
     * - however, the new trigger need not have the same name as the old trigger.
     *
     * @param triggerKey
     * identity of the trigger to replace
     * @param newTrigger
     * The new [ITrigger] to be stored.
     * @return `null` if a `Trigger` with the given name
     * &amp; group was not found and removed from the store (and the new
     * trigger is therefore not stored), otherwise the first fire time of
     * the newly scheduled trigger is returned.
     */
    @Throws(SchedulerException::class)
    fun rescheduleJob(triggerKey: TriggerKey, newTrigger: ITrigger): Instant

    /**
     * Add the given `Job` to the Scheduler - with no associated
     * `Trigger`. The `Job` will be 'dormant' until it is
     * scheduled with a `Trigger`, or
     * `Scheduler.triggerJob()` is called for it.
     *
     *
     * The `Job` must by definition be 'durable', if it is not,
     * SchedulerException will be thrown.
     *
     *
     * @see .addJob
     * @throws SchedulerException
     * if there is an internal Scheduler error, or if the Job is not
     * durable, or a Job with the same name already exists, and
     * `replace` is `false`.
     */
    @Throws(SchedulerException::class)
    fun addJob(jobDetail: JobDetail, replace: Boolean)

    /**
     * Add the given `Job` to the Scheduler - with no associated
     * `Trigger`. The `Job` will be 'dormant' until it is
     * scheduled with a `Trigger`, or
     * `Scheduler.triggerJob()` is called for it.
     *
     *
     * With the `storeNonDurableWhileAwaitingScheduling` parameter set
     * to `true`, a non-durable job can be stored. Once it is
     * scheduled, it will resume normal non-durable behavior (i.e. be deleted once
     * there are no remaining associated triggers).
     *
     *
     * @throws SchedulerException
     * if there is an internal Scheduler error, or if the Job is not
     * durable, or a Job with the same name already exists, and
     * `replace` is `false`.
     */
    @Throws(SchedulerException::class)
    fun addJob(
        jobDetail: JobDetail,
        replace: Boolean,
        storeNonDurableWhileAwaitingScheduling: Boolean,
    )

    /**
     * Delete the identified `Job` from the Scheduler - and any
     * associated `Trigger`s.
     *
     * @return true if the Job was found and deleted.
     * @throws SchedulerException
     * if there is an internal Scheduler error.
     */
    @Throws(SchedulerException::class)
    fun deleteJob(jobKey: JobKey): Boolean

    /**
     * Delete the identified `Job`s from the Scheduler - and any
     * associated `Trigger`s.
     *
     *
     * Note that while this bulk operation is likely more efficient than invoking
     * `deleteJob(JobKey jobKey)` several times, it may have the
     * adverse affect of holding data locks for a single long duration of time
     * (rather than lots of small durations of time).
     *
     *
     * @return true if all of the Jobs were found and deleted, false if one or
     * more were not deleted.
     * @throws SchedulerException
     * if there is an internal Scheduler error.
     */
    @Throws(SchedulerException::class)
    fun deleteJobs(jobKeys: List<JobKey>): Boolean

    /**
     * Trigger the identified `[com.helger.quartz.IJobDetail]`
     * (execute it now).
     */
    @Throws(SchedulerException::class)
    fun triggerJob(jobKey: JobKey)

    /**
     * Trigger the identified `[com.helger.quartz.IJobDetail]`
     * (execute it now).
     *
     * @param data
     * the (possibly `null`) JobDataMap to be associated with
     * the trigger that fires the job immediately.
     */
    @Throws(SchedulerException::class)
    fun triggerJob(jobKey: JobKey, data: JobDataMap)

    /**
     * Pause the `[com.helger.quartz.IJobDetail]` with the given
     * key - by pausing all of its current `Trigger`s.
     *
     * @see .resumeJob
     */
    @Throws(SchedulerException::class)
    fun pauseJob(jobKey: JobKey)

    /**
     * Pause all of the `[com.helger.quartz.IJobDetail]s` in the
     * matching groups - by pausing all of their `Trigger`s.
     *
     *
     * The Scheduler will "remember" the groups paused, and impose the pause on
     * any new jobs that are added to any of those groups until it is resumed.
     *
     *
     *
     * NOTE: There is a limitation that only exactly matched groups can be
     * remembered as paused. For example, if there are pre-existing job in groups
     * "aaa" and "bbb" and a matcher is given to pause groups that start with "a"
     * then the group "aaa" will be remembered as paused and any subsequently
     * added jobs in group "aaa" will be paused, however if a job is added to
     * group "axx" it will not be paused, as "axx" wasn't known at the time the
     * "group starts with a" matcher was applied. HOWEVER, if there are
     * pre-existing groups "aaa" and "bbb" and a matcher is given to pause the
     * group "axx" (with a group equals matcher) then no jobs will be paused, but
     * it will be remembered that group "axx" is paused and later when a job is
     * added in that group, it will become paused.
     *
     *
     * @param matcher
     * The matcher to evaluate against know groups
     * @throws SchedulerException
     * On error
     * @see .resumeJobs
     */
    @Throws(SchedulerException::class)
    fun pauseJobs(matcher: GroupMatcher<JobKey>)

    /**
     * Pause the `[ITrigger]` with the given key.
     *
     * @see .resumeTrigger
     */
    @Throws(SchedulerException::class)
    fun pauseTrigger(triggerKey: TriggerKey)

    /**
     * Pause all of the `[ITrigger]s` in the groups matching.
     *
     *
     * The Scheduler will "remember" all the groups paused, and impose the pause
     * on any new triggers that are added to any of those groups until it is
     * resumed.
     *
     *
     *
     * NOTE: There is a limitation that only exactly matched groups can be
     * remembered as paused. For example, if there are pre-existing triggers in
     * groups "aaa" and "bbb" and a matcher is given to pause groups that start
     * with "a" then the group "aaa" will be remembered as paused and any
     * subsequently added triggers in that group be paused, however if a trigger
     * is added to group "axx" it will not be paused, as "axx" wasn't known at the
     * time the "group starts with a" matcher was applied. HOWEVER, if there are
     * pre-existing groups "aaa" and "bbb" and a matcher is given to pause the
     * group "axx" (with a group equals matcher) then no triggers will be paused,
     * but it will be remembered that group "axx" is paused and later when a
     * trigger is added in that group, it will become paused.
     *
     *
     * @param matcher
     * The matcher to evaluate against know groups
     * @throws SchedulerException
     * @see .resumeTriggers
     */
    @Throws(SchedulerException::class)
    fun pauseTriggers(matcher: GroupMatcher<TriggerKey>)

    /**
     * Resume (un-pause) the `[com.helger.quartz.IJobDetail]`
     * with the given key.
     *
     *
     * If any of the `Job`'s`Trigger` s missed one or more
     * fire-times, then the `Trigger`'s misfire instruction will be
     * applied.
     *
     *
     * @see .pauseJob
     */
    @Throws(SchedulerException::class)
    fun resumeJob(jobKey: JobKey)

    /**
     * Resume (un-pause) all of the
     * `[com.helger.quartz.IJobDetail]s` in matching groups.
     *
     *
     * If any of the `Job` s had `Trigger` s that missed one
     * or more fire-times, then the `Trigger`'s misfire instruction
     * will be applied.
     *
     *
     * @param matcher
     * The matcher to evaluate against known paused groups
     * @throws SchedulerException
     * On error
     * @see .pauseJobs
     */
    @Throws(SchedulerException::class)
    fun resumeJobs(matcher: GroupMatcher<JobKey>)

    /**
     * Resume (un-pause) the `[ITrigger]` with the given key.
     *
     *
     * If the `Trigger` missed one or more fire-times, then the
     * `Trigger`'s misfire instruction will be applied.
     *
     *
     * @see .pauseTrigger
     */
    @Throws(SchedulerException::class)
    fun resumeTrigger(triggerKey: TriggerKey)

    /**
     * Resume (un-pause) all of the `[ITrigger]s` in matching
     * groups.
     *
     *
     * If any `Trigger` missed one or more fire-times, then the
     * `Trigger`'s misfire instruction will be applied.
     *
     *
     * @param matcher
     * The matcher to evaluate against know paused groups
     * @throws SchedulerException
     * On error
     * @see .pauseTriggers
     */
    @Throws(SchedulerException::class)
    fun resumeTriggers(matcher: GroupMatcher<TriggerKey>)

    /**
     * Pause all triggers - similar to calling
     * `pauseTriggerGroup(group)` on every group, however, after using
     * this method `resumeAll()` must be called to clear the
     * scheduler's state of 'remembering' that all new triggers will be paused as
     * they are added.
     *
     *
     * When `resumeAll()` is called (to un-pause), trigger misfire
     * instructions WILL be applied.
     *
     *
     * @see .resumeAll
     * @see .pauseTriggers
     * @see .standby
     */
    @Throws(SchedulerException::class)
    fun pauseAll()

    /**
     * Resume (un-pause) all triggers - similar to calling
     * `resumeTriggerGroup(group)` on every group.
     *
     *
     * If any `Trigger` missed one or more fire-times, then the
     * `Trigger`'s misfire instruction will be applied.
     *
     *
     * @see .pauseAll
     */
    @Throws(SchedulerException::class)
    fun resumeAll()

    @get:Throws(SchedulerException::class)
    val jobGroupNames: List<String>

    /**
     * Get the keys of all the `[com.helger.quartz.IJobDetail]s`
     * in the matching groups.
     *
     * @param matcher
     * Matcher to evaluate against known groups
     * @return Set of all keys matching
     * @throws SchedulerException
     * On error
     */
    @Throws(SchedulerException::class)
    fun getJobKeys(matcher: GroupMatcher<JobKey>): Set<JobKey>

    /**
     * Get all `[ITrigger]` s that are associated with the
     * identified `[com.helger.quartz.IJobDetail]`.
     *
     *
     * The returned Trigger objects will be snap-shots of the actual stored
     * triggers. If you wish to modify a trigger, you must re-store the trigger
     * afterward (e.g. see [.rescheduleJob]).
     *
     */
    @Throws(SchedulerException::class)
    fun getTriggersOfJob(jobKey: JobKey): List<out ITrigger>

    @get:Throws(SchedulerException::class)
    val triggerGroupNames: List<String>

    /**
     * Get the names of all the `[ITrigger]s` in the given group.
     *
     * @param matcher
     * Matcher to evaluate against known groups
     * @return List of all keys matching
     * @throws SchedulerException
     * On error
     */
    @Throws(SchedulerException::class)
    fun getTriggerKeys(matcher: GroupMatcher<TriggerKey>): Set<TriggerKey>

    @get:Throws(SchedulerException::class)
    val pausedTriggerGroups: Set<String>

    /**
     * Get the `[IJobDetail]` for the `Job` instance
     * with the given key.
     *
     *
     * The returned JobDetail object will be a snap-shot of the actual stored
     * JobDetail. If you wish to modify the JobDetail, you must re-store the
     * JobDetail afterward (e.g. see [.addJob]).
     *
     */
    @Throws(SchedulerException::class)
    fun getJobDetail(jobKey: JobKey): JobDetail

    /**
     * Get the `[ITrigger]` instance with the given key.
     *
     *
     * The returned Trigger object will be a snap-shot of the actual stored
     * trigger. If you wish to modify the trigger, you must re-store the trigger
     * afterward (e.g. see [.rescheduleJob]).
     *
     */
    @Throws(SchedulerException::class)
    fun getTrigger(triggerKey: TriggerKey): ITrigger

    /**
     * Get the current state of the identified `[ITrigger]`.
     *
     * @see ITrigger.ETriggerState
     */
    @Throws(SchedulerException::class)
    fun getTriggerState(triggerKey: TriggerKey): ETriggerState

    /**
     * Add (register) the given `Calendar` to the Scheduler.
     *
     * @param updateTriggers
     * whether or not to update existing triggers that referenced the
     * already existing calendar so that they are 'correct' based on the
     * new trigger.
     * @throws SchedulerException
     * if there is an internal Scheduler error, or a Calendar with the
     * same name already exists, and `replace` is
     * `false`.
     */
    @Throws(SchedulerException::class)
    fun addCalendar(
        calName: String,
        calendar: Calendar,
        replace: Boolean,
        updateTriggers: Boolean,
    )

    /**
     * Delete the identified `Calendar` from the Scheduler.
     *
     *
     * If removal of the `Calendar` would result in
     * `Trigger`s pointing to non-existent calendars, then a
     * `SchedulerException` will be thrown.
     *
     *
     * @return true if the Calendar was found and deleted.
     * @throws SchedulerException
     * if there is an internal Scheduler error, or one or more triggers
     * reference the calendar
     */
    @Throws(SchedulerException::class)
    fun deleteCalendar(calName: String): Boolean

    /**
     * Get the `[ICalendar]` instance with the given name.
     */
    @Throws(SchedulerException::class)
    fun getCalendar(calName: String): Calendar

    @get:Throws(SchedulerException::class)
    val calendarNames: List<String>

    /**
     * Request the interruption, within this Scheduler instance, of all currently
     * executing instances of the identified `Job`, which must be an
     * implementor of the `InterruptableJob` interface.
     *
     *
     * If more than one instance of the identified job is currently executing, the
     * `InterruptableJob#interrupt()` method will be called on each
     * instance. However, there is a limitation that in the case that
     * `interrupt()` on one instances throws an exception, all
     * remaining instances (that have not yet been interrupted) will not have
     * their `interrupt()` method called.
     *
     *
     *
     * This method is not cluster aware. That is, it will only interrupt instances
     * of the identified InterruptableJob currently executing in this Scheduler
     * instance, not across the entire cluster.
     *
     *
     * @return true if at least one instance of the identified job was found and
     * interrupted.
     * @throws UnableToInterruptJobException
     * if the job does not implement `InterruptableJob`, or
     * there is an exception while interrupting the job.
     * @see IInterruptableJob.interrupt
     * @see .getCurrentlyExecutingJobs
     * @see .interrupt
     */
    @Throws(UnableToInterruptJobException::class)
    fun interrupt(jobKey: JobKey): Boolean

    /**
     * Request the interruption, within this Scheduler instance, of the identified
     * executing `Job` instance, which must be an implementor of the
     * `InterruptableJob` interface.
     *
     *
     * This method is not cluster aware. That is, it will only interrupt instances
     * of the identified InterruptableJob currently executing in this Scheduler
     * instance, not across the entire cluster.
     *
     *
     * @param fireInstanceId
     * the unique identifier of the job instance to be interrupted (see
     * [IJobExecutionContext.getFireInstanceId]
     * @return true if the identified job instance was found and interrupted.
     * @throws UnableToInterruptJobException
     * if the job does not implement `InterruptableJob`, or
     * there is an exception while interrupting the job.
     * @see IInterruptableJob.interrupt
     * @see .getCurrentlyExecutingJobs
     * @see IJobExecutionContext.getFireInstanceId
     * @see .interrupt
     */
    @Throws(UnableToInterruptJobException::class)
    fun interrupt(fireInstanceId: String): Boolean

    /**
     * Determine whether a [IJob] with the given identifier already exists
     * within the scheduler.
     *
     * @param jobKey
     * the identifier to check for
     * @return true if a Job exists with the given identifier
     * @throws SchedulerException
     */
    @Throws(SchedulerException::class)
    fun checkExists(jobKey: JobKey): Boolean

    /**
     * Determine whether a [ITrigger] with the given identifier already
     * exists within the scheduler.
     *
     * @param triggerKey
     * the identifier to check for
     * @return true if a Trigger exists with the given identifier
     * @throws SchedulerException
     */
    @Throws(SchedulerException::class)
    fun checkExists(triggerKey: TriggerKey): Boolean

    /**
     * Clears (deletes!) all scheduling data - all [IJob]s,
     * [ITrigger]s [ICalendar]s.
     *
     * @throws SchedulerException
     */
    @Throws(SchedulerException::class)
    fun clear()

    companion object {
        /**
         * A (possibly) useful constant that can be used for specifying the group that
         * `Job` and `Trigger` instances belong to.
         */
        val DEFAULT_GROUP: String = "DEFAULT_GROUP"

        /**
         * A constant `Trigger` group name used internally by the scheduler
         * - clients should not use the value of this constant ("RECOVERING_JOBS") for
         * the name of a `Trigger`'s group.
         *
         * @see com.helger.quartz.IJobDetail.requestsRecovery
         */
        const val DEFAULT_RECOVERY_GROUP: String = "RECOVERING_JOBS"

        /**
         * A constant `Trigger` group name used internally by the scheduler
         * - clients should not use the value of this constant ("FAILED_OVER_JOBS")
         * for the name of a `Trigger`'s group.
         *
         * @see com.helger.quartz.IJobDetail.requestsRecovery
         */
        const val DEFAULT_FAIL_OVER_GROUP: String = "FAILED_OVER_JOBS"

        /**
         * A constant `JobDataMap` key that can be used to retrieve the
         * name of the original `Trigger` from a recovery trigger's data
         * map in the case of a job recovering after a failed scheduler instance.
         *
         * @see com.helger.quartz.IJobDetail.requestsRecovery
         */
        const val FAILED_JOB_ORIGINAL_TRIGGER_NAME: String = "QRTZ_FAILED_JOB_ORIG_TRIGGER_NAME"

        /**
         * A constant `JobDataMap` key that can be used to retrieve the
         * group of the original `Trigger` from a recovery trigger's data
         * map in the case of a job recovering after a failed scheduler instance.
         *
         * @see com.helger.quartz.IJobDetail.requestsRecovery
         */
        const val FAILED_JOB_ORIGINAL_TRIGGER_GROUP: String = "QRTZ_FAILED_JOB_ORIG_TRIGGER_GROUP"

        /**
         * A constant `JobDataMap` key that can be used to retrieve the
         * fire time of the original `Trigger` from a recovery trigger's
         * data map in the case of a job recovering after a failed scheduler instance.
         *
         *
         * Note that this is the time the original firing actually occurred, which may
         * be different from the scheduled fire time - as a trigger doesn't always
         * fire exactly on time.
         *
         *
         * @see com.helger.quartz.IJobDetail.requestsRecovery
         */
        const val FAILED_JOB_ORIGINAL_TRIGGER_FIRETIME_IN_MILLISECONDS: String =
            "QRTZ_FAILED_JOB_ORIG_TRIGGER_FIRETIME_IN_MILLISECONDS_AS_STRING"

        /**
         * A constant `JobDataMap` key that can be used to retrieve the
         * scheduled fire time of the original `Trigger` from a recovery
         * trigger's data map in the case of a job recovering after a failed scheduler
         * instance.
         *
         *
         * Note that this is the time the original firing was scheduled for, which may
         * be different from the actual firing time - as a trigger doesn't always fire
         * exactly on time.
         *
         *
         * @see com.helger.quartz.IJobDetail.requestsRecovery
         */
        const val FAILED_JOB_ORIGINAL_TRIGGER_SCHEDULED_FIRETIME_IN_MILLISECONDS: String =
            "QRTZ_FAILED_JOB_ORIG_TRIGGER_SCHEDULED_FIRETIME_IN_MILLISECONDS_AS_STRING"
    }
}

